home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / wnos / wn941101 / ftpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-05  |  26.4 KB  |  1,128 lines

  1. /* 941005 Added ftp type command ftype
  2.  *        by dc0hk
  3.  */
  4.  
  5. /* FTP client (interactive user) code */
  6. #include <stdio.h>
  7. #include <ctype.h>
  8. #include <sys/stat.h>
  9. #include "global.h"
  10. #include "mbuf.h"
  11. #include "session.h"
  12. #include "cmdparse.h"
  13. #include "proc.h"
  14. #include "tty.h"
  15. #include "socket.h"
  16. #include "ftp.h"
  17. #include "ftpcli.h"
  18. #include "commands.h"
  19. #include "netuser.h"
  20. #include "dirutil.h"
  21. #include "files.h"
  22.  
  23. #define    DIRBUF    256
  24.  
  25.  
  26. static int dotype __ARGS((int argc,char *argv[],void *p));
  27. static int doview __ARGS((int argc,char *argv[],void *p));
  28. static int near getresp __ARGS((struct ftpcli *ftp,int mincode));
  29. static void near sendport __ARGS((int s,struct sockaddr_in *socket));
  30. static char Notsess[] = "Not an FTP session!\n";
  31. static int Ftp_type = ASCII_TYPE;
  32. static int Ftp_logbsize = 8;
  33.  
  34.  
  35. /* Common code to LIST/NLST/RETR and mget
  36.  * Returns number of bytes received if successful
  37.  * Returns -1 on error
  38.  */
  39. static long near
  40. getsub(ftp,command,remotename,localname,offset)
  41. struct ftpcli *ftp;
  42. char *command,*remotename,*localname;
  43. long offset;
  44. {
  45.     unsigned long total;
  46.     FILE *fp;
  47.     int cnt = 0, resp, i, control, savmode, vsave, prevstate, typewait = 0;
  48.     char *mode;
  49.     struct sockaddr_in lsocket, lcsocket;
  50.     int32 startclk, rate;
  51.  
  52.     if(ftp == NULLFTP)
  53.         return -1;
  54.  
  55.     control = ftp->control;
  56.     savmode = ftp->type;
  57.  
  58.     switch(ftp->type){
  59.     case IMAGE_TYPE:
  60.     case LOGICAL_TYPE:
  61.         mode = WRITE_BINARY;
  62.         break;
  63.     case ASCII_TYPE:
  64.         mode = WRITE_TEXT;
  65.         break;
  66.     }
  67.     if(offset)
  68.         mode = "rb+";
  69.  
  70.     /* Open the file */
  71.     if(localname == NULLCHAR){
  72.         fp = NULLFILE;
  73.     } else if((fp = open_file(localname,mode,0,1)) == NULLFILE)
  74.         return -1;
  75.  
  76.     if (fp) {
  77.         if(fseek(fp,offset,SEEK_SET))   {
  78.             tprintf("Can't position %s: %s\n",localname,sys_errlist[errno]);
  79.             fclose(fp);
  80.             return -1;
  81.         }
  82.     }
  83.     /* Open the data connection */
  84.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  85.     listen(ftp->data,0);    /* Accept only one connection */
  86.     prevstate = ftp->state;
  87.     ftp->state = RECEIVING_STATE;
  88.  
  89.     /* Send TYPE message, if necessary */
  90.     if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
  91.         /* Directory listings are always in ASCII */
  92.         ftp->type = ASCII_TYPE;
  93.     }
  94.     if(ftp->typesent != ftp->type){
  95.         switch(ftp->type){
  96.         case LOGICAL_TYPE:
  97.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  98.             break;
  99.         case ASCII_TYPE:
  100.         case IMAGE_TYPE:
  101.             usprintf(control,"TYPE %s\n",(ftp->type == ASCII_TYPE) ? "A" : "I");
  102.             break;
  103.         }
  104.         ftp->typesent = ftp->type;
  105.         if(!ftp->batch){
  106.             resp = getresp(ftp,200);
  107.             if(resp == -1 || resp > 299)
  108.                 goto failure;
  109.         } else
  110.             typewait = 1;
  111.     }
  112.     /* Send the PORT message. Use the IP address
  113.      * on the local end of our control connection.
  114.      */
  115.     i = SOCKSIZE;
  116.     getsockname(ftp->data,(char *)&lsocket,&i); /* Get port number */
  117.     i = SOCKSIZE;
  118.     getsockname(ftp->control,(char *)&lcsocket,&i);
  119.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  120.     sendport(control,&lsocket);
  121.     if(!ftp->batch){
  122.         /* Get response to PORT command */
  123.         resp = getresp(ftp,200);
  124.         if(resp == -1 || resp > 299)
  125.             goto failure;
  126.     }
  127.     /* Generate the command to start the transfer */
  128.     usputs(control,command);
  129.     if(remotename != NULLCHAR)
  130.         usprintf(control," %s",remotename);
  131.     if(offset)
  132.         usprintf(control," %ld",offset);
  133.     usputs(control,"\n");
  134.  
  135.     if(ftp->batch){
  136.         /* Get response to TYPE command, if sent */
  137.         if(typewait){
  138.             resp = getresp(ftp,200);
  139.             if(resp == -1 || resp > 299)
  140.                 goto failure;
  141.         }
  142.         /* Get response to PORT command */
  143.         resp = getresp(ftp,200);
  144.         if(resp == -1 || resp > 299)
  145.             goto failure;
  146.     }
  147.     /* Get the intermediate "150" response */
  148.     resp = getresp(ftp,100);
  149.     if(resp == -1 || resp >= 400)
  150.         goto failure;
  151.  
  152.     /* Wait for the server to open the data connection */
  153.     ftp->data = accept(ftp->data,NULLCHAR,&cnt);
  154.     startclk = msclock();
  155.  
  156.     /* If output is to the screen, temporarily disable hash marking */
  157.     vsave = ftp->verbose;
  158.     if(vsave >= V_HASH && fp == NULLFILE)
  159.         ftp->verbose = V_NORMAL;
  160.     total = recvfile(fp,ftp->data,ftp->type,ftp->verbose >= V_HASH ? ftp->verbose : 0);
  161.     /* Immediately close the data connection; some servers (e.g., TOPS-10)
  162.      * wait for the data connection to close completely before returning
  163.      * the completion message on the control channel
  164.      */
  165.     close_s(ftp->data);
  166.     ftp->data = -1;
  167.  
  168. #ifdef    CPM
  169.     if(fp != NULLFILE && ftp->type == ASCII_TYPE)
  170.         putc(CTLZ,fp);
  171. #endif
  172.  
  173.     if(fp != NULLFILE && fp != stdout)
  174.         fclose(fp);
  175.     if(remotename == NULLCHAR)
  176.         remotename = "";
  177.     if(total == -1)
  178.         tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
  179.  
  180.     /* Get the "Sent" message */
  181.     getresp(ftp,200);
  182.  
  183.     if(total != -1 && ftp->verbose >= V_SHORT) {
  184.         startclk = msclock() - startclk;
  185.         rate = (startclk != 0) ? (total*1000)/startclk : 0;
  186.         tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
  187.             command,remotename,total,startclk/1000,rate);
  188.     }
  189.     ftp->state = prevstate;
  190.     ftp->verbose = vsave;
  191.     ftp->type = savmode;
  192.     return total;
  193.  
  194. failure:
  195.     /* Error, quit */
  196.     if(fp != NULLFILE && fp != stdout)
  197.         fclose(fp);
  198.     close_s(ftp->data);
  199.     ftp->data = -1;
  200.     ftp->state = prevstate;
  201.     ftp->type = savmode;
  202.     return -1;
  203. }
  204.  
  205. /* Common code to put, mput */
  206. static long near
  207. putsub(ftp,remotename,localname)
  208. register struct ftpcli *ftp;
  209. char *remotename,*localname;
  210. {
  211.     int i = SOCKSIZE, resp, typewait = 0, prevstate;
  212.     unsigned long total;
  213.     FILE *fp;
  214.     struct sockaddr_in lsocket, lcsocket;
  215.     int32 startclk, rate;
  216.  
  217.     int control = ftp->control;
  218.     char *mode = (ftp->type == IMAGE_TYPE) ? READ_BINARY : READ_TEXT;
  219.  
  220.     /* Open the file */
  221.     if((fp = fopen(localname,mode)) == NULLFILE)
  222.         return -1;
  223.  
  224.     if(ftp->type == ASCII_TYPE && isbinary(fp)){
  225.         tprintf("Warning: type is ASCII and %s appears to be binary\n",localname);
  226.     }
  227.     /* Open the data connection */
  228.     ftp->data = socket(AF_INET,SOCK_STREAM,0);
  229.     listen(ftp->data,0);
  230.     prevstate = ftp->state;
  231.     ftp->state = SENDING_STATE;
  232.  
  233.     /* Send TYPE message, if necessary */
  234.     if(ftp->typesent != ftp->type){
  235.         switch(ftp->type){
  236.         case LOGICAL_TYPE:
  237.             usprintf(control,"TYPE L %d\n",ftp->logbsize);
  238.             break;
  239.         case ASCII_TYPE:
  240.         case IMAGE_TYPE:
  241.             usprintf(control,"TYPE %s\n",(ftp->type == ASCII_TYPE) ? "A" : "I");
  242.         }
  243.         ftp->typesent = ftp->type;
  244.         /* Get response to TYPE command */
  245.         if(!ftp->batch){
  246.             resp = getresp(ftp,200);
  247.             if(resp == -1 || resp > 299){
  248.                 goto failure;
  249.             }
  250.         } else {
  251.             typewait = 1;
  252.         }
  253.     }
  254.     /* Send the PORT message. Use the IP address
  255.      * on the local end of our control connection.
  256.      */
  257.     getsockname(ftp->data,(char *)&lsocket,&i);
  258.     i = SOCKSIZE;
  259.     getsockname(ftp->control,(char *)&lcsocket,&i);
  260.     lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
  261.     sendport(control,&lsocket);
  262.     if(!ftp->batch){
  263.         /* Get response to PORT command */
  264.         resp = getresp(ftp,200);
  265.         if(resp == -1 || resp > 299){
  266.             goto failure;
  267.         }
  268.     }
  269.     /* Generate the command to start the transfer */
  270.     usprintf(control,"STOR %s\n",remotename);
  271.  
  272.     if(ftp->batch){
  273.         /* Get response to TYPE command, if sent */
  274.         if(typewait){
  275.             resp = getresp(ftp,200);
  276.             if(resp == -1 || resp > 299){
  277.                 goto failure;
  278.             }
  279.         }
  280.         /* Get response to PORT command */
  281.         resp = getresp(ftp,200);
  282.         if(resp == -1 || resp > 299){
  283.             goto failure;
  284.         }
  285.     }
  286.     /* Get the intermediate "150" response */
  287.     resp = getresp(ftp,100);
  288.     if(resp == -1 || resp >= 400){
  289.         goto failure;
  290.     }
  291.  
  292.     /* Wait for the data connection to open. Otherwise the first
  293.      * block of data would go out with the SYN, and this may confuse
  294.      * some other TCPs
  295.      */
  296.     accept(ftp->data,NULLCHAR,(int *)NULL);
  297.  
  298.     startclk = msclock();
  299.  
  300.     total = sendfile(fp,ftp->data,ftp->type,ftp->verbose >= V_HASH ? ftp->verbose : 0);
  301.     close_s(ftp->data);
  302.     ftp->data = -1;
  303.     fclose(fp);
  304.  
  305.     if(total == -1)
  306.         tprintf("STOR %s: Error/abort during data transfer\n",remotename);
  307.  
  308.     getresp(ftp,200);
  309.  
  310.     if(total != -1 && ftp->verbose >= V_SHORT) {
  311.         startclk = msclock() - startclk;
  312.         rate = (startclk != 0) ? (total*1000)/startclk : 0;
  313.         tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
  314.             remotename,total,startclk/1000,rate);
  315.     }
  316.     ftp->state = prevstate;
  317.     return total;
  318.  
  319. failure:
  320.     /* Error, quit */
  321.     fclose(fp);
  322.     close_s(ftp->data);
  323.     ftp->data = -1;
  324.     ftp->state = prevstate;
  325.     return -1;
  326. }
  327.  
  328. /* send PORT message */
  329. static void near
  330. sendport(s,socket)
  331. int s;
  332. struct sockaddr_in *socket;
  333. {
  334.     /* Send PORT a,a,a,a,p,p message */
  335.     usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
  336.         hibyte(hiword(socket->sin_addr.s_addr)),
  337.         lobyte(hiword(socket->sin_addr.s_addr)),
  338.         hibyte(loword(socket->sin_addr.s_addr)),
  339.         lobyte(loword(socket->sin_addr.s_addr)),
  340.         hibyte(socket->sin_port),
  341.         lobyte(socket->sin_port));
  342. }
  343.  
  344. /* Wait for, read and display response from FTP server. Return the result code.
  345.  */
  346. static int near
  347. getresp(ftp,mincode)
  348. struct ftpcli *ftp;
  349. int mincode;    /* Keep reading until at least this code comes back */
  350. {
  351.     char line[LINELEN];
  352.     int rval = -1;
  353.  
  354.     usflush(ftp->control);
  355.     for(;;){
  356.         /* Get line */
  357.         if(recvline(ftp->control,line,LINELEN) == -1){
  358.             break;
  359.         }
  360.         rip(line);        /* Remove cr/lf */
  361.         if((rval = atoi(line)) >= 400 || ftp->verbose >= V_NORMAL)
  362.             tprintf("%s\n",line);    /* Display to user */
  363.  
  364.         /* Messages with dashes are continued */
  365.         if(line[3] != '-' && rval >= mincode)
  366.             break;
  367.     }
  368.     return rval;
  369. }
  370.  
  371. /* Issue a prompt and read a line from the user */
  372. static int near
  373. getline(sp,prompt,buf,n)
  374. struct session *sp;
  375. char *prompt;
  376. char *buf;
  377. int n;
  378. {
  379.     /* If there's something already there, don't issue prompt */
  380.     if(socklen(sp->input,0) == 0)
  381.         tputs(prompt);
  382.  
  383.     usflush(sp->output);
  384.     usflush(sp->s);
  385.     return recvline(sp->input,buf,n);
  386. }
  387.  
  388. /* Attempt to log in the user whose name is in ftp->username and password
  389.  * in pass
  390.  */
  391. static char * near
  392. ftpcli_login(ftp,host)
  393. struct ftpcli *ftp;
  394. char *host;
  395. {
  396.     char buf[LINELEN], *cp, *cp1;
  397.     FILE *fp;
  398.  
  399.     if((fp = fopen(Hostfile,READ_TEXT)) == NULLFILE){
  400.         return NULLCHAR;
  401.     }
  402.  
  403.     while(fgets(buf,LINELEN,fp),!feof(fp)){
  404.         if(buf[0] == '#')
  405.             continue;    /* Comment */
  406.         rip(buf);
  407.         if((cp = strchr(buf,' ')) == NULLCHAR)
  408.             /* Bogus entry */
  409.             continue;
  410.         *cp++ = '\0';        /* Now points to user name */
  411.         if(strcmp(host,buf) == 0)
  412.             break;        /* Found host name */
  413.     }
  414.     if(feof(fp)){
  415.         /* User name not found in file */
  416.         fclose(fp);
  417.         return NULLCHAR;
  418.     }
  419.     fclose(fp);
  420.  
  421.     /* Look for space after user field in file */
  422.     if((cp1 = strchr(cp,' ')) == NULLCHAR)
  423.         /* if not there then we'll prompt */
  424.         ftp->password = NULLCHAR;
  425.     else
  426.         *cp1++ = '\0';        /* Now points to password */
  427.     if(strcmp(cp,"*") == 0)
  428.         cp1 = "anonymous";
  429.     ftp->password = strxdup(cp1);
  430.     return strxdup(cp);
  431. }
  432.  
  433.  
  434. /* ------------------------- FTP-Client Subcmds ------------------------- */
  435.  
  436. /* Abort a GET or PUT operation in progress.
  437.  * Note: this will leave the partial file on the local or remote system
  438.  * This function is called from config.h
  439.  */
  440. int
  441. doabort(argc,argv,p)
  442. int argc;
  443. char *argv[];
  444. void *p;
  445. {
  446.     struct session *sp = (struct session *)p;
  447.     struct ftpcli *ftp = sp->cb.ftp;
  448.  
  449.     /* Default is the current session, but it can be overridden with
  450.      * an argument.
  451.      */
  452.     if(argc > 1)
  453.         sp = sessptr(argv[1]);
  454.  
  455.     if(sp == NULLSESSION || sp->type != FTP) {
  456.         tputs(Notsess);
  457.  
  458.     } else {
  459.         switch(ftp->state){
  460.         case COMMAND_STATE:
  461.             tputs("No active transfer\n");
  462.             return 1;
  463.         case SENDING_STATE:        /* defined as 1 */
  464.         case RECEIVING_STATE:    /* defined as 2 */
  465.             /* Send a premature EOF.
  466.              * Unfortunately we can't just reset the connection
  467.              * since the remote side might end up waiting forever
  468.              * for us to send something.
  469.              * If receiving state just blow away the socket
  470.              */
  471.             shutdown(ftp->data,ftp->state);
  472.             ftp->abort = 1;
  473.             return 0;
  474.         }
  475.     }
  476.     return -1;
  477. }
  478.  
  479. static int
  480. doascii(argc,argv,p)
  481. int argc;
  482. char *argv[];
  483. void *p;
  484. {
  485.     char *args[2];
  486.  
  487.     args[1] = "A";
  488.     return dotype(2,args,p);
  489. }
  490.  
  491. /* Enable/disable command batching */
  492. static int
  493. dobatch(argc,argv,p)
  494. int argc;
  495. char *argv[];
  496. void *p;
  497. {
  498.     struct ftpcli *ftp;
  499.  
  500.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  501.         return -1;
  502.     setbool(&ftp->batch,"FTP batching",argc,argv);
  503.     return 0;
  504. }
  505.  
  506. static int
  507. dobinary(argc,argv,p)
  508. int argc;
  509. char *argv[];
  510. void *p;
  511. {
  512.     char *args[2];
  513.  
  514.     args[1] = "I";
  515.     return dotype(2,args,p);
  516. }
  517.  
  518. /* Translate 'cd' to 'cwd' for convenience */
  519. static int
  520. doftpcd(argc,argv,p)
  521. int argc;
  522. char *argv[];
  523. void *p;
  524. {
  525.     struct ftpcli *ftp;
  526.  
  527.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  528.         return -1;
  529.     usprintf(ftp->control,"CWD %s\n",argv[1]);
  530.     return getresp(ftp,200);
  531. }
  532.  
  533. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  534. static int
  535. doget(argc,argv,p)
  536. int argc;
  537. char *argv[];
  538. void *p;
  539. {
  540.     struct ftpcli *ftp;
  541.     char *remotename, *localname;
  542.  
  543.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  544.         return -1;
  545.     remotename = argv[1];
  546.     localname = (argc < 3) ? remotename : argv[2];
  547.     getsub(ftp,"RETR",remotename,localname,0L);
  548.     return 0;
  549. }
  550.  
  551. /* Set verbosity to high (convenience command) */
  552. static int
  553. dohash(argc,argv,p)
  554. int argc;
  555. char *argv[];
  556. void *p;
  557. {
  558.     struct ftpcli *ftp;
  559.  
  560.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  561.         return -1;
  562.     ftp->verbose = V_HASH;
  563.     return 0;
  564. }
  565.  
  566. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  567. static int
  568. dolist(argc,argv,p)
  569. int argc;
  570. char *argv[];
  571. void *p;
  572. {
  573.     struct ftpcli *ftp;
  574.     char *remotename,*localname;
  575.  
  576.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  577.         return -1;
  578.     remotename = argv[1];
  579.     localname = (argc < 3) ? NULLCHAR : argv[2];
  580.     getsub(ftp,"LIST",remotename,localname,0L);
  581.     return 0;
  582. }
  583.  
  584. /* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
  585. static int
  586. dols(argc,argv,p)
  587. int argc;
  588. char *argv[];
  589. void *p;
  590. {
  591.     struct ftpcli *ftp;
  592.     char *remotename, *localname;
  593.  
  594.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  595.         return -1;
  596.     remotename = argv[1];
  597.     localname = (argc < 3) ? NULLCHAR : argv[2];
  598.     getsub(ftp,"NLST",remotename,localname,0L);
  599.     return 0;
  600. }
  601.  
  602. /* Get a collection of files */
  603. static int
  604. domget(argc,argv,p)
  605. int argc;
  606. char *argv[];
  607. void *p;
  608. {
  609.     struct ftpcli *ftp;
  610.     FILE *files, *filel;
  611.     char tmpname[80], *buf, *local;
  612. #ifdef    MSDOS
  613.     char *c;
  614. #endif
  615.     int i, inlist;
  616.     long r;
  617.  
  618.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  619.         return -1;
  620.  
  621.     tmpnam(tmpname);
  622.     buf = mxallocw(DIRBUF);
  623.  
  624.     ftp->state = RECEIVING_STATE;
  625.  
  626.     for(i = 1; i < argc; i++) {
  627.       if(ftp->abort)
  628.         break;
  629.       if(argv[i][0] == '@') {
  630.          inlist = 1;
  631.          if((filel = open_file(&argv[i][1],"r",ftp->control,0)) == NULLFILE)
  632.             continue;
  633.          if((files = open_file(tmpname,"w",ftp->control,0)) == NULLFILE) {
  634.             fclose(filel);
  635.             continue;
  636.          }
  637.          while(fgets(buf,DIRBUF,filel) != NULLCHAR)
  638.             fputs(buf,files);
  639.          fclose(files);
  640.          fclose(filel);
  641.          if((files = open_file(tmpname, "r",ftp->control,0)) == NULLFILE){
  642.             fclose(filel);
  643.             continue;
  644.          }
  645.       } else {
  646.         inlist = 0;
  647.         r = getsub(ftp,"NLST",argv[i],tmpname,0L);
  648.         if(ftp->abort)
  649.             break;
  650.         if(r == -1 || (files = open_file(tmpname,"r",0,1)) == NULLFILE){
  651.             unlink(tmpname);
  652.             continue;
  653.         }
  654.       }
  655.         /* The tmp file now contains a list of the remote files.
  656.          * If any cannot be read, it must be because we were aborted
  657.          * or reset locally, so break out if a transfer fails.
  658.          */
  659.         while(fgets(buf,DIRBUF,files) != NULLCHAR){
  660.             rip(buf);
  661.             local = strxdup(buf);
  662. #ifdef    MSDOS
  663.             if(inlist){
  664.                 strrev(local);
  665.                 strtok(local, "\\/[]<>,?#~()&%");
  666.                 strrev(local);
  667.             }
  668.             if((c = strstr(local, ".")) != NULLCHAR) {
  669.                 c++;
  670.                 c = strtok(c, ".");            /* remove 2nd period if any */
  671.             }
  672. #endif
  673.             getsub(ftp,"RETR",buf,local,0L);
  674.             xfree(local);
  675.             if(ftp->abort) {
  676.                 ftp->abort = 0;
  677.                 fclose(files);
  678.                 unlink(tmpname);
  679.                 ftp->state = COMMAND_STATE;
  680.                 xfree(buf);
  681.                 return 1;
  682.             }
  683.         }
  684.         fclose(files);
  685.         unlink(tmpname);
  686.     }
  687.     xfree(buf);
  688.     ftp->abort = 0;
  689.     ftp->state = COMMAND_STATE;
  690.     return 0;
  691. }
  692.  
  693. /* Translate 'mkdir' to 'xmkd' for convenience */
  694. static int
  695. domkdir(argc,argv,p)
  696. int argc;
  697. char *argv[];
  698. void *p;
  699. {
  700.     struct ftpcli *ftp;
  701.  
  702.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  703.         return -1;
  704.     usprintf(ftp->control,"XMKD %s\n",argv[1]);
  705.     return getresp(ftp,200);
  706. }
  707.  
  708. /* Put a collection of files */
  709. static int
  710. domput(argc,argv,p)
  711. int argc;
  712. char *argv[];
  713. void *p;
  714. {
  715.     struct ftpcli *ftp;
  716.     FILE *files;
  717.     int i;
  718.     char *buf, tmpname[80];
  719.  
  720.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  721.         return -1;
  722.  
  723.     tmpnam(tmpname);
  724.     if((files = open_file(tmpname,"w+",0,1)) == NULLFILE){
  725.         unlink(tmpname);
  726.         return -1;
  727.     }
  728.  
  729.     for(i = 1; i < argc; i++)
  730.         getdir(argv[i],0,files);
  731.  
  732.     rewind(files);
  733.     buf = mxallocw(DIRBUF);
  734.     ftp->state = SENDING_STATE;
  735.     while(fgets(buf,DIRBUF,files) != NULLCHAR){
  736.         rip(buf);
  737.         putsub(ftp,buf,buf);
  738.         if(ftp->abort)
  739.             break;
  740.     }
  741.     fclose(files);
  742.     unlink(tmpname);
  743.     xfree(buf);
  744.     ftp->state = COMMAND_STATE;
  745.     ftp->abort = 0;
  746.     return 0;
  747. }
  748.  
  749. /* NO-OP function */
  750. static int
  751. donothing(argc,argv,p)
  752. int argc;
  753. char *argv[];
  754. void *p;
  755. {
  756.     return 0;
  757. }
  758.  
  759. /* Send a file. Syntax: put <local name> [<remote name>] */
  760. static int
  761. doput(argc,argv,p)
  762. int argc;
  763. char *argv[];
  764. void *p;
  765. {
  766.     struct ftpcli *ftp;
  767.     char *remotename, *localname;
  768.  
  769.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  770.         return -1;
  771.     localname = argv[1];
  772.     remotename = (argc < 3) ? localname : argv[2];
  773.     putsub(ftp,remotename,localname);
  774.     return 0;
  775. }
  776.  
  777. /* Close session */
  778. static int
  779. doquit(argc,argv,p)
  780. int argc;
  781. char *argv[];
  782. void *p;
  783. {
  784.     struct ftpcli *ftp;
  785.  
  786.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  787.         return -1;
  788.     usputs(ftp->control,"QUIT\n");
  789.     getresp(ftp,200);            /* Get the closing message */
  790.     getresp(ftp,200);            /* Wait for the server to close */
  791.     return -1;
  792. }
  793.  
  794. /* Translate 'rmdir' to 'xrmd' for convenience */
  795. static int
  796. dormdir(argc,argv,p)
  797. int argc;
  798. char *argv[];
  799. void *p;
  800. {
  801.     struct ftpcli *ftp;
  802.  
  803.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  804.         return -1;
  805.     usprintf(ftp->control,"XRMD %s\n",argv[1]);
  806.     return getresp(ftp,200);
  807. }
  808.  
  809. /* Start receive transfer restart. Syntax: get <remote name> [<local name>] */
  810. static int
  811. dorest(argc,argv,p)
  812. int argc;
  813. char *argv[];
  814. void *p;
  815. {
  816.     struct ftpcli *ftp;
  817.     char *remotename, *localname;
  818.     long offset;
  819.     struct stat st;
  820.  
  821.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  822.         return -1;
  823.  
  824.     /*-------------------------------------------------------------------*
  825.     *  local and remotename required
  826.     *--------------------------------------------------------------------*/
  827.     remotename = argv[1];
  828.     localname = (argc < 3) ? remotename : argv[2];
  829.  
  830.     if (stat(localname,&st))   {
  831.         tprintf("Cannot find %s\n",localname);
  832.         return -1;
  833.     }
  834.     /*-------------------------------------------------------------------*
  835.     * adjust offset to latest 1K Boundary
  836.     *--------------------------------------------------------------------*/
  837.     offset = st.st_size & 0xfffffc00L;
  838.     /*-------------------------------------------------------------------*
  839.     * let 'em swing
  840.     *--------------------------------------------------------------------*/
  841.     getsub(ftp,"REST",remotename,localname,offset);
  842.     return 0;
  843. }
  844.  
  845. /* Handle "type" command from user */
  846. static int
  847. dotype(argc,argv,p)
  848. int argc;
  849. char *argv[];
  850. void *p;
  851. {
  852.     struct ftpcli *ftp;
  853.  
  854.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  855.         return -1;
  856.     if(argc < 2){
  857.         switch(ftp->type){
  858.         case IMAGE_TYPE:
  859.         case ASCII_TYPE:
  860.             tprintf("%s\n",(ftp->type == ASCII_TYPE) ? "Ascii" : "Image");
  861.             break;
  862.         case LOGICAL_TYPE:
  863.             tprintf("Logical bytesize %u\n",ftp->logbsize);
  864.             break;
  865.         }
  866.         return 0;
  867.     }
  868.     switch(tolower(*argv[1])) {
  869.     case 'i':
  870.     case 'b':
  871.         ftp->typesent = ftp->type = IMAGE_TYPE;
  872.         usputs(ftp->control,"TYPE I\n");
  873.         break;
  874.     case 'a':
  875.         ftp->typesent = ftp->type = ASCII_TYPE;
  876.         usputs(ftp->control,"TYPE A\n");
  877.         break;
  878.     case 'l':
  879.         ftp->typesent = ftp->type = LOGICAL_TYPE;
  880.         ftp->logbsize = atoi(argv[2]);
  881.         usprintf(ftp->control,"TYPE L %s\n",argv[2]);
  882.         break;
  883.     default:
  884.         tprintf("Invalid type %s\n",argv[1]);
  885.         return 0;
  886.     }
  887.     return getresp(ftp,200);
  888. }
  889.  
  890.   
  891. /* Handle "ftype" command */
  892. int
  893. doftype(argc,argv,p)
  894. int argc;
  895. char *argv[];
  896. void *p;
  897. {
  898.     if(argc < 2){
  899.         tputs("Ftp initial TYPE is ");
  900.         switch(Ftp_type){
  901.             case IMAGE_TYPE:
  902.                 tputs("Image\n");
  903.                 break;
  904.             case ASCII_TYPE:
  905.                 tputs("Ascii\n");
  906.                 break;
  907.             case LOGICAL_TYPE:
  908.                 tprintf("Logical bytesize %u\n",Ftp_logbsize);
  909.                 break;
  910.         }
  911.         return 0;
  912.     }
  913.     switch(*argv[1]){
  914.         case 'i':
  915.         case 'I':
  916.         case 'b':
  917.         case 'B':
  918.             Ftp_type = IMAGE_TYPE;
  919.             break;
  920.         case 'a':
  921.         case 'A':
  922.             Ftp_type = ASCII_TYPE;
  923.             break;
  924.         case 'L':
  925.         case 'l':
  926.             Ftp_type = LOGICAL_TYPE;
  927.             Ftp_logbsize = atoi(argv[2]);
  928.             break;
  929.         default:
  930.             tprintf("Invalid type %s\n",argv[1]);
  931.             return 1;
  932.     }
  933.     return 0;
  934. }
  935.  
  936. /* Control verbosity level */
  937. static int
  938. doverbose(argc,argv,p)
  939. int argc;
  940. char *argv[];
  941. void *p;
  942. {
  943.     struct ftpcli *ftp;
  944.  
  945.     if((ftp = (struct ftpcli *)p) == NULLFTP)
  946.         return -1;
  947.     setshort(&ftp->verbose,"Verbose",argc,argv);
  948.     return 0;
  949. }
  950. /* Start view transfer. Syntax: view <remote name> */
  951. static int
  952. doview(argc,argv,p)
  953. int argc;
  954. char *argv[];
  955. void *p;
  956. {
  957.     char *remotename;
  958.     register struct ftpcli  *ftp;
  959.           
  960.         ftp = (struct ftpcli *)p;
  961.         if(ftp == NULLFTP) {
  962.             tputs(Notsess);
  963.                 return 1;
  964.                         }
  965.                   remotename = argv[1];
  966.                                             
  967.                 getsub(ftp,"RETR",remotename,NULLCHAR);
  968.                 return 0;
  969.                         }
  970.                                                     
  971.  
  972. /* Handle top-level FTP command */
  973. int
  974. doftp(argc,argv,p)
  975. int argc;
  976. char *argv[];
  977. void *p;
  978. {
  979.     struct session *sp;
  980.     struct ftpcli ftp;
  981.     struct sockaddr_in fsocket;
  982.     int resp, vsave, control;
  983.     char *buf, *bufsav, *cp;
  984.  
  985.     struct cmds Ftpcmds[] = {
  986.         "",         donothing,  0, 0, NULLCHAR,
  987.         "ascii",    doascii,    0, 0, NULLCHAR,
  988.         "batch",    dobatch,    0, 0, NULLCHAR,
  989.         "binary",   dobinary,   0, 0, NULLCHAR,
  990.         "cd",       doftpcd,    0, 2, "cd <dir>",
  991.         "dir",      dolist,     0, 0, NULLCHAR,
  992.         "list",     dolist,     0, 0, NULLCHAR,
  993.         "get",      doget,      0, 2, "get <remotefile> <localfile>",
  994.         "hash",     dohash,     0, 0, NULLCHAR,
  995.         "ls",       dols,       0, 0, NULLCHAR,
  996.         "mget",     domget,     0, 2, "mget <file> [<file> ...]",
  997.         "mkdir",    domkdir,    0, 2, "mkdir <dir>",
  998.         "mput",     domput,     0, 2, "mput <file> [<file> ...]",
  999.         "nlst",     dols,       0, 0, NULLCHAR,
  1000.         "quit",     doquit,     0, 0, NULLCHAR,
  1001.         "rmdir",    dormdir,    0, 2, "rmdir <dir>",
  1002.         "put",      doput,      0, 2, "put <localfile> <remotefile>",
  1003.         "restart",  dorest,     0, 2, "restart <remotefile> <localfile>",
  1004.         "type",     dotype,     0, 0, NULLCHAR,
  1005.         "verbose",  doverbose,  0, 0, NULLCHAR,
  1006.         "view",        doview,     0, 2, "view <remotefile>",        
  1007.         NULLCHAR,   NULLFP,     0, 0, NULLCHAR,
  1008.     };
  1009.  
  1010.     /* Allocate a session control block */
  1011.     if((sp = newsession(argv[1],FTP,0,1)) == NULLSESSION){
  1012.         tputs(Nosess);
  1013.         return -1;
  1014.     }
  1015.     memset((char *)&ftp,0,sizeof(ftp));
  1016.     ftp.verbose = V_NORMAL;
  1017.     ftp.control = ftp.data = -1;
  1018.  
  1019.     sp->cb.ftp = &ftp;    /* Downward link */
  1020.     ftp.session = sp;    /* Upward link */
  1021.  
  1022.     fsocket.sin_family = AF_INET;
  1023.     fsocket.sin_port = (argc < 3) ? IPPORT_FTP : atoi(argv[2]);
  1024.     tprintf("Resolving %s... ",sp->name);
  1025.  
  1026.     if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
  1027.         tprintf(Badhost,sp->name);
  1028.         goto quit2;
  1029.     }
  1030.     /* Open the control connection */
  1031.     if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
  1032.         tputs(Nosocket);
  1033.         goto quit2;
  1034.     }
  1035.     sockmode(sp->s,SOCK_ASCII);
  1036.     setflush(sp->s,-1);
  1037.     tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
  1038.     if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
  1039.         goto quit;
  1040.     tprintf("FTP session connected to %s\n",sp->name);
  1041.     log(sp->s,"FTP  connect");
  1042.  
  1043.     /* Wait for greeting from server */
  1044.     resp = getresp(&ftp,200);
  1045.     if(resp >= 400)
  1046.         goto quit;
  1047.     /* Now process responses and commands */
  1048.     buf = mxallocw(LINELEN);
  1049.     while(resp != -1){
  1050.         if(resp == 220){
  1051.             /* Sign-on banner; prompt for and send USER command */
  1052.             if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR){
  1053.                 getline(sp,"Enter user name: ",buf,LINELEN);
  1054.                 if(buf[0] != '\n'){
  1055.                     usprintf(control,"USER %s",buf);
  1056.                     resp = getresp(&ftp,200);
  1057.                 } else
  1058.                     resp = 200;    /* dummy */
  1059.             } else {
  1060.                 usprintf(control,"USER %s\n",cp);
  1061.                 xfree(cp);
  1062.                 resp = getresp(&ftp,200);
  1063.             }
  1064.         } else if(resp == 331) { /* Password prompt; get password */
  1065.             if(ftp.password == NULLCHAR) {
  1066.                 /* turn off echo */
  1067.                 sp->ttystate.echo = 0;
  1068.                 getline(sp,"Password: ",buf,LINELEN);
  1069.                 sp->ttystate.echo = 1;
  1070.                 tputs("\n");
  1071.                 if(buf[0] != '\n'){
  1072.                     usprintf(control,"PASS %s",buf);
  1073.                     resp = getresp(&ftp,200);
  1074.                 } else {
  1075.                     resp = 200;    /* dummy */
  1076.                 }
  1077.             } else {
  1078.                 usprintf(control,"PASS %s\n",ftp.password);
  1079.                 resp = getresp(&ftp,200);
  1080.             }
  1081.         } else {
  1082.             /* Test the control channel first */
  1083.             if(sockstate(control) == NULLCHAR) {
  1084.                 xfree(buf);
  1085.                 goto quit;
  1086.             }
  1087.             getline(sp,"ftp> ",buf,LINELEN);
  1088.  
  1089.             /* Copy because cmdparse modifies the original */
  1090.             bufsav = strxdup(buf);
  1091.             if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
  1092.                 /* Valid command, free buffer and get another */
  1093.                 xfree(bufsav);
  1094.             } else {
  1095.                 /* Not a local cmd, send to remote server */
  1096.                 usputs(control,bufsav);
  1097.                 xfree(bufsav);
  1098.                 /* Enable display of server response */
  1099.                 vsave = ftp.verbose;
  1100.                 ftp.verbose = V_NORMAL;
  1101.                 resp = getresp(&ftp,200);
  1102.                 ftp.verbose = vsave;
  1103.             }
  1104.         }
  1105.     }
  1106.     xfree(buf);
  1107. quit:
  1108.     cp = sockerr(control);
  1109.     tprintf("FTP session closed: %s\n",cp != NULLCHAR ? cp : "EOF");
  1110.  
  1111.     if(ftp.fp != NULLFILE && ftp.fp != stdout)
  1112.         fclose(ftp.fp);
  1113.     if(ftp.data != -1)
  1114.         close_s(ftp.data);
  1115.     if(ftp.control != -1)
  1116.         close_s(ftp.control);
  1117.     keywait(NULLCHAR,1);
  1118.     if(ftp.session != NULLSESSION)
  1119.         freesession(ftp.session);
  1120.     if(ftp.password != NULLCHAR)
  1121.         xfree(ftp.password);
  1122.     return 0;
  1123. quit2:
  1124.     keywait(NULLCHAR,1);
  1125.     freesession(sp);
  1126.     return -1;
  1127. }
  1128.  
  1129.